package com.seaengine.net
{
	import flash.utils.ByteArray;
	import flash.utils.Endian;

	public class Codec extends ByteArray
	{
		private const SHORT_LENGTH:uint = 2;
		private const INT_LENGTH:uint	= 4;
		
		
		public function Codec()
		{
			super();
			this.endian = Endian.LITTLE_ENDIAN;
		}
		
		public function encodeInt8(data:int):Codec       //char
		{
			this.writeByte(data);
			return this;
		}
		public function decodeInt8(index:int=-1):int
		{
			if ( !this.bytesAvailable )
				return undefined;
			if(-1 != index)
				this.seek(index);
			return this.readByte();
		}
		
		public function encodeUInt8(data:int):Codec       //char
		{
			this.writeByte(data);
			return this;
		}
		public function decodeUInt8(index:int=-1):int
		{
			if ( !this.bytesAvailable )
				return undefined;
			if(-1 != index)
				this.seek(index);
			return this.readUnsignedByte();
		}
		
		public function encodeInt16(data:int):Codec    //short
		{
			this.writeShort(Common.htons(data));
			return this;
		}
		
		public function encodeUInt16(data:int):Codec
		{
			return encodeInt16(data);
		}
		
		
		public function decodeInt16(index:int=-1):int
		{
			if(-1 != index)
				this.seek(index);
			if(this.bytesAvailable < 2)
			{
				return undefined;
			}
			return Common.ntohs(this.readShort());
		}
		
		public function decodeUInt16(index:int = -1):uint
		{
			return this.readUnsignedShort();
			
		}
		
		
		public function encodeInt32(data:int):Codec     //int
		{
			this.writeInt(Common.htonl(data));
			return this;
		}
		public function decodeInt32(index:int=-1):int
		{
			if(-1 != index)
				this.seek(index);
			if(this.bytesAvailable < 4)
			{
				return undefined;
			}
			return Common.ntohl(this.readInt());
			//return this.readInt();
		}
		
		/* public function decodeInt64(index:int=-1):int
		{
			if(-1 != index)
				this.seek(index);
			if(this.bytesAvailable < 8)
			{
				return undefined;
			}
			return Common.ntohl(int(this.readDouble()));
		} */
		
		public function encodeUInt64(_val:Int64):Codec
		{
			this.writeUnsignedInt(_val.low);
			this.writeUnsignedInt( _val.high);
			return this;
		}
		
		public function decodeUInt64():Int64
		{
			//fixme: no debug
			var _low:uint = this.readUnsignedInt();
			var _high:uint = this.readUnsignedInt();
			
			return new Int64(_high,_low);
		}		
		
		public function encodeInt64(_val:Int64):Codec
		{
			this.writeUnsignedInt(_val.low);
			this.writeInt(_val.high);
			return this;		
		}
		
		public function decodeInt64():Int64
		{
			var _low:uint = this.readUnsignedInt();
			var _high:int = this.readInt();
			return new Int64(_high,_low);		
		}	
		
		
		public function encodeUInt32(data:uint):Codec     //int
		{
			this.writeUnsignedInt(Common.htonl(data));
			return this;
		}
		/**
		 * 
		 * @param index
		 * @return 
		 * 
		 */
		public function decodeUInt32(index:int=-1):uint
		{
			if(-1 != index)
				this.seek(index);
			return Common.htonl(this.readUnsignedInt());
		}
		
		public function encodeUnicode(data:String):Codec
		{
			this.writeMultiByte(data,"unicode");
			return this;
		}
		public function decodeUnicode(index:int=-1,len:uint=0):String
		{
			if(-1 != index)
				this.seek(index)
			return (len)?this.readMultiByte(len,"unicode"):this.readMultiByte(this.length,"unicode");
		}
		
		public function encodeBuffer(data:String):Codec     //anybuffer   不能包含中文
		{
			this.writeMultiByte(data,"us-ascii");
			return this;
		}
		/**
		 * 
		 * @param index
		 * @param len
		 * @return 
		 * 
		 */
		public function decodeBuffer(index:int=-1,len:uint=0):String
		{
			if(-1 != index)
				this.seek(index);
			return (len)?this.readMultiByte(len,"us-ascii"):this.readMultiByte(this.length,"us-ascii");
		}
		
		/**
		 * 
		 * @param data
		 * @return 
		 * 
		 */
		public function encodeString(data:String):Codec     //字符串   支持中文
		{
			this.writeUTFBytes(data);
			return this;
		}
		/**
		 * 
		 * @param index
		 * @param len
		 * @return 
		 * 
		 */
		public function decodeString(index:int=-1,len:uint=0):String
		{
			if(-1 != index)
				this.seek(index);
			if((this.length - this.position) < len)
			{
				return "";
			}
			if(len > 0)
			{
				return this.readUTFBytes(len)
			}
			return "";
		}
		
		/**
		 * 
		 * @param buffer
		 * @param index
		 * @return 
		 * 
		 */
		public function encodeBytes(buffer:ByteArray,index:uint=0):Codec
		{
			if(!index)
			{
				this.writeBytes(buffer);
			}else{
				buffer.position = index;
				for(var i:uint = index; i < buffer.length; ++i)
				{
					this.encodeInt8(buffer.readByte());
				}
			}
			return this;
		}
		/**
		 * 
		 * @param index
		 * @param len
		 * @return 
		 * 
		 */
		public function decodeBytes(index:int,len:uint=0):JoyPortBuffer
		{
			if(index != -1)
				this.seek(index)
			var res:JoyPortBuffer = new JoyPortBuffer();
			(len)?this.readBytes(res,index,len):this.readBytes(res,index,this.length);
			return res;
		}
		
		/**
		 * 
		 * @param index
		 * @return 
		 * 
		 */
		public function seek(index:int=0):int
		{
			this.position = (-1 == index)? (this.length - 1):index;
			return index;
		}
		
		/**
		 * 
		 * @param lenType
		 * @param src
		 * @param zero
		 * @return 
		 * 
		 */
		public function encodeStringEx(src:String):Codec
		{
			this.encodeUInt32(getByteLen(src));
			this.encodeString(src);
			return this;
		}
		
		public static function getByteLen(str:String):int {
            var len:int = -1;
            if(str != null){
            var ba:ByteArray =new ByteArray;
            ba.writeMultiByte(str,"utf-8");
            len = ba.length;
            }
            return len;
        }

		
		/**
		 * 
		 * @param lenType
		 * @param charSet
		 * @return 
		 * 
		 */
		public function decodeStringEx(lenType:int,charSet:String="utf-8"):String
		{
			if ( !lenType )
				return null;
			
			var len:int = 0;
			switch ( lenType )
			{
				case 1:
					len = this.decodeInt8();
					break;
				case 2:
					len = this.decodeInt16();
					break;
				case 4:
					len = this.decodeInt32();
					break;
			}
			if ( this.bytesAvailable < len )
				return "";
			return this.readMultiByte(len,charSet);
		}
		
		/**
		 * 
		 * @param lenType
		 * @param src
		 * @return 
		 * 
		 */
		public function encodeBytesEx(lenType:int,src:Codec):Codec
		{
			if ( !lenType || !src)
				return null;
			
			switch ( lenType )
			{
				case 1:
					if (src.length > 0xff )
						return null;
					this.encodeInt8(src.length);
					break;
				case 2:
					if (src.length > 0xffff)
						return null;
					this.encodeInt16(src.length);
					break;
				case 4:
					if (src.length > 0xffffffff)
						return null;
					this.encodeInt32(src.length);
					break;
			}
			
			return this.encodeBytes(src);
		}
		
		public function decodeBytesEx(lenType:int):JoyPortBuffer
		{
			if ( !lenType )
				return null;
			
			var len:int = 0;
			switch ( lenType )
			{
				case 1:
					len = this.decodeInt8();
					break;
				case 2:
					len = this.decodeInt16();
					break;
				case 4:
					len = this.decodeInt32();
					break;
			}
			return this.decodeBytes(this.position,len);
		}
		
		public function encodeFloat(data:Number):Codec
		{
			this.writeFloat(data);		
			return this;
		}
		
		public function decodeFloat():Number
		{
			return this.readFloat();
		}
				
		public function encodeDouble(data:Number):Codec
		{
			this.writeDouble(data);		
			return this;
		}
		public function decodeDouble():Number
		{
			return this.readDouble();
		}
		
		
		
	}
}